#include<cstdio>
#include<string.h>
#include<string>
#include<iostream>
#include<fstream>
#include<vector>
#include<cstdlib>
#include<set>
#include<map>
#include<stack>
#include<utility>
#include<climits>
#include<time.h>
#include"Inference.h"

int main(int argc, char* argv[]){

	const char *filename = argv[1];
	cout<<filename<<endl;

	string sLine = "";
	std::ifstream infile;

	vector< vector<pair<int,float> > > Data=vector< vector<pair<int,float> > >();
	infile.open (filename, std::ifstream::in); int count=0;
	vector<factor> Net=vector<factor>();
	cout<<"Reading Data..";
	int nNode=-1, nEdge = -1;


	count = 0;
	getline(infile, sLine); getline(infile, sLine);
	nNode = (int)atoi(sLine.c_str());

	short *cardinality=(short*)malloc(nNode*sizeof(short));

	for (int line=0; line<1 && !infile.eof(); line++){
		getline(infile, sLine);
		count++;
		vector<string> temp =  vector<string>();
		string word = "";
		for(unsigned i=0; i<sLine.length();i++){
			if(sLine[i]==' '){

				temp.push_back(word);
				word = "";

			}else{
				word+=sLine[i];
			}

		}
		temp.push_back(word);

		for(int j=0;j<nNode;j++) cardinality[j]=(short)atoi(temp[j].c_str());
		temp.clear();
	}
	 getline(infile, sLine); nEdge = (int)atoi(sLine.c_str());
	cout<<"Number of Node:" <<nNode<<endl;
	cout<<"Number of Edge:" <<nEdge<<endl;

//*********Build the Markov Network
	 Graph NodeToFactor(nNode); Graph G(nNode);

	 int factorcount=0;

	for (int l=0; l<nEdge && !infile.eof(); l++){
		getline(infile, sLine);
		if(factorcount%10000==0) cout<< factorcount<<endl;
		count++;
		vector<string> temp =  vector<string>();
		string word = "";
		for(unsigned i=0; i<sLine.length();i++){
			if(sLine[i]=='\t'){
				temp.push_back(word);
				word = "";

			}else{
				word+=sLine[i];
			}

		}
		temp.push_back(word);

//typedef pair<vector<int> ,vector<pair<vector<short>,float > > > factor;
		int cliquesize = (int)atoi(temp[0].c_str());
		if(cliquesize==2) {
			G.insert((int)atoi(temp[1].c_str()),(int)atoi(temp[2].c_str()));
			G.insert((int)atoi(temp[2].c_str()),(int)atoi(temp[1].c_str()));


		}else if(cliquesize==3){
//			int node1 = (int)atoi(temp[1].c_str());
//			int node2 = (int)atoi(temp[2].c_str());
//			int node3 = (int)atoi(temp[3].c_str());


//			if(!G.isConnected(node1,node2)) G.insert(node1,node2);
//			if(!G.isConnected(node2,node1)) G.insert(node2,node1);
//			if(!G.isConnected(node1,node3)) G.insert(node1,node3);
//			if(!G.isConnected(node3,node1)) G.insert(node3,node1);
//			if(!G.isConnected(node2,node3)) G.insert(node2,node3);
//			if(!G.isConnected(node3,node2)) G.insert(node3,node2);


		}

		vector<int> cliquenodes=vector<int>();
		for(int j=0;j<cliquesize;j++){
			int NodeID = (int)atoi(temp[j+1].c_str());
			cliquenodes.push_back(NodeID);
			NodeToFactor.insert(NodeID, factorcount);
		}

		vector<pair<vector<short>,float > > values = vector<pair<vector<short>,float > >();
		factorcount++;
		Net.push_back(factor(cliquenodes,values));
		temp.clear(); cliquenodes.clear();
	}
	cout<<"Number of Clique Factor:" <<factorcount<<endl;


//*********Build the Factors*************


	for (int l=0; l<nEdge && !infile.eof(); l++){
		factorcount++;
		if(factorcount%10000==0) cout<< factorcount<<endl;
		getline(infile, sLine);
		getline(infile, sLine);
		int cardfactor = 1;
		for(unsigned j=0;j<Net[l].first.size();j++) cardfactor *= cardinality[Net[l].first[j]];
		if(cardfactor!=(int)atoi(sLine.c_str())) { cout<<"ERROR: Factor cardinality mismatch at factor:" <<l<<endl;
			cout<<"Nodes in the Factor: ";
			for(unsigned j=0;j<Net[l].first.size();j++) cout <<j<<" ";
			cout<<endl;
			exit(EXIT_FAILURE);
		}
		getline(infile, sLine);
		vector<string> temp =  vector<string>();
		string word = "";
		for(unsigned i=0; i<sLine.length();i++){
			if(sLine[i]==' '){
				temp.push_back(word);
				word = "";

			}else{
				word+=sLine[i];
			}

		}
		temp.push_back(word);
//typedef pair<vector<int> ,vector<pair<vector<short>,float > > > factor;

		if(Net[l].first.size()==1){
			for(int u=0;u<cardinality[Net[l].first[0]];u++){
				float prob = (float)atof(temp[u].c_str());
				if(prob!=0.0){
					// Make the <value, prob> pair
					vector<short> x = vector<short>();
					x.push_back(u);
					pair<vector<short>,float > value_prob (x,prob);
					//Insert it in the factor
					Net[l].second.push_back(value_prob);
					x.clear();
				}
			}
		}else if(Net[l].first.size()==2){
			for(int u=0;u<cardinality[Net[l].first[0]];u++){
				for(int v=0;v<cardinality[Net[l].first[1]];v++){
					float prob = (float)atof(temp[u*cardinality[Net[l].first[1]] + v].c_str());
					if(prob!=0.0){
						// Make the <value, prob> pair
						vector<short> x = vector<short>();
						x.push_back(u); x.push_back(v);
						pair<vector<short>,float > value_prob (x,prob);
						//Insert it in the factor
						Net[l].second.push_back(value_prob);
						x.clear();
					}
				}
			}
		}else if(Net[l].first.size()==3){

			for(int u=0;u<cardinality[Net[l].first[0]];u++){
				for(int v=0;v<cardinality[Net[l].first[1]];v++){
					for(int w=0;w<cardinality[Net[l].first[2]];w++){

						float prob = (float)atof(temp[(u*cardinality[Net[l].first[1]] + v)*cardinality[Net[l].first[2]] + w].c_str());
						if(prob!=0.0){
							// Make the <value, prob> pair
							vector<short> x = vector<short>();
							x.push_back(u); x.push_back(v); x.push_back(w);
							pair<vector<short>,float > value_prob (x,prob);
							//Insert it in the factor
							Net[l].second.push_back(value_prob);
							x.clear();
						}
					}
				}
			}
		}else {
			cout<<"Cannot Handle clique size of more than 3, current clique size: "<< Net[l].first.size()<<endl;
		}


		temp.clear();

	}
	infile.close();
	cout<<"Finished Loading Factors"<<endl;



	//G.show();
	FILE *fpdeg = fopen(argv[4],"w+"); string str = "";
	char *buff0 = (char*)malloc(sizeof(int));

	for(int i=0;i<nNode;i++){
		sprintf(buff0, "%d",G.getDegree(i));
		fprintf(fpdeg,"%s\n",string(buff0+'\0').c_str());
	}
	fclose(fpdeg);



	//G.getEliminationOrder(elimorder,fillin,InducedWidth);

//	FILE *fpelim = fopen(argv[2],"w+"); string str = "";
//	char *buff = (char*)malloc(sizeof(int));
//	for(unsigned i=0;i<elimorder.size();i++){
//		sprintf(buff, "%d",elimorder[i]); str+=string(buff+'\0');
//		str+=",";
//		sprintf(buff, "%d",fillin[i]); str+=string(buff+'\0');
//		fprintf(fpelim,"%s\n",str.c_str());
//		str="";
//
//	}
//	fclose(fpelim);



//	//G.clear();




	char *buff = (char*)malloc(sizeof(int));
//	infile.open (argv[2], std::ifstream::in);
//
//	sLine = ""; getline(infile,sLine);
//	int nEvid = (int)atoi(sLine.c_str());

//	#pragma omp parallel for
//	for(int I=0;I<nEvid;I++){
//		cout<<"Evidence: " <<I <<endl;
//		getline(infile,sLine);
//
//		vector<string> temp =  vector<string>();
//		string word = "";
//		for(unsigned i=0; i<sLine.length();i++){
//			if(sLine[i]==' '){
//				temp.push_back(word);
//				word = "";
//
//			}else{
//				word+=sLine[i];
//				}
//
//		}
//		temp.push_back(word);
//		vector<pair<int,short> > evid = vector<pair<int,short> >();
//		int nvar = (int)atoi(temp[0].c_str());
//
//		for(int i=0;i<nvar;i++){
//			evid.push_back(pair<int,short >((int)atoi(temp[2*i+1].c_str()), (short)atoi(temp[2*i+2].c_str())) );
//		}
//
//		Network M(Net,NodeToFactor,nNode, cardinality);
//		M.evidence(evid);
//		sprintf(buff,"%d",I);
//		string fname = string(argv[3]+'\0')+"/Sample" + string(buff+'\0') + ".txt";
//		M.Gibbs(fname.c_str(),500);
//
//
//
//
//
//	}

	Network M(Net,NodeToFactor,nNode, cardinality);
	vector<int> Tree1 = vector<int>();
	Tree1.push_back(0); Tree1.push_back(1); Tree1.push_back(2); Tree1.push_back(3);
	Tree1.push_back(7); Tree1.push_back(11); Tree1.push_back(10); Tree1.push_back(9); Tree1.push_back(15);

	vector<int> Tree2 = vector<int>();
	Tree2.push_back(4); Tree2.push_back(5); Tree2.push_back(6); Tree2.push_back(8);
	Tree2.push_back(12); Tree2.push_back(13); Tree2.push_back(14);

	vector< vector<int> > Trees = vector< vector<int> >();
	Trees.push_back(Tree1); Trees.push_back(Tree2);



	M.BlockedGibbs(Trees); M.show();
//	M.show();
//	sprintf(buff,"%d",0);
//	string fname = string(argv[3]+'\0')+"/Sample" + string(buff+'\0') + ".txt";
//	M.Gibbs(fname.c_str(),50000);

	vector<int> elimorder = vector<int>();
	for(int i=0;i<nNode;i++) elimorder.push_back(i);
	for(int i=0;i<nNode;i++){
		Network Markov(Net,NodeToFactor,nNode, cardinality);
		factor F = Markov.getMarginal(elimorder,i);
		for(unsigned j=0;j<F.second.size();j++) cout<<F.second[j].first[0]<<": "<<F.second[j].second<<" ";
		cout<<endl;


	}






	//markovnet.Gibbs(argv[2],100000);

//	for(int i = 2;i<nNode;i++){
//		cout<<i<<": "<<endl;
//		Network M(Net,NodeToFactor,nNode, cardinality);
//		M.evidence(evid);
//		factor F = M.getMarginal(elimorder,i);
//		for(unsigned j=0;j<F.second.size();j++) cout<<F.second[j].first[0]<<": "<<F.second[j].second<<" ";
//		cout<<endl;
//	}
//
//	char *buff = (char*)malloc(sizeof(int));
//	for(int i=0;i<4;i++){
//	Network M(Net,NodeToFactor,nNode, cardinality); M.evidence(evid);
//	sprintf(buff,"%d",i);
//	string fname = string(argv[2]+'\0') + string(buff + '\0');
//	M.Gibbs(fname.c_str(),100000);
//	}



	cout<<"*********** Finish **************"<<endl;

	return 0;
}
